package org.kjs.common.clone.util.implement;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

import org.kjs.common.clone.Clone;
import org.kjs.common.clone.exception.CloneNotSupportedException;
import org.kjs.common.clone.util.CloneEngine;

/**
 * Created by IntelliJ IDEA.
 * User: KJS
 * Date: 11-8-22
 * Time: 下午8:08
 * To change this template use File | Settings | File Templates.
 */
public class CloneEngineer implements CloneEngine {

    public static final String ARRAY_CLONE_METHOD_NAME = "clone";
    public static final Class<?> [] CLONE_METHOD_PARAMETERS_TYPE = {};
    public static final Object [] CLONE_METHOD_PARAMETERS = {};

    public <V> boolean isJavaBasicDataType (V object) {
        boolean isJavaBasicDataType = false;

        if (null != object) {
            String className = object.getClass().getName();
            if ("byte".equals(className) ||
                    "boolean".equals(className) ||
                    "short".equals(className) ||
                    "char".equals(className) ||
                    "int".equals(className) ||
                    "long".equals(className) ||
                    "float".equals(className) ||
                    "double".equals(className)) {
                isJavaBasicDataType = true;
            }
        } else {
            isJavaBasicDataType = true;
        }

        return isJavaBasicDataType;
    }

    public <V> boolean isJavaBasicDataWrapperType (V object) {
        boolean isJavaBasicDataWrapperType = false;

        if (null != object) {
            Class<?> clazz = object.getClass();
            if (Byte.class.equals(clazz) ||
                    Boolean.class.equals(clazz) ||
                    Short.class.equals(clazz) ||
                    Character.class.equals(clazz) ||
                    Integer.class.equals(clazz) ||
                    Long.class.equals(clazz) ||
                    Float.class.equals(clazz) ||
                    Double.class.equals(clazz)) {
                isJavaBasicDataWrapperType = true;
            }
        }

        return isJavaBasicDataWrapperType;
    }

    public <V> V cloneJavaBasicDataWrapper (V originalJavaBasicDataWrapper) {
        V cloneJavaBasicDataWrapper = null;

        if (null != originalJavaBasicDataWrapper) {
            if (isJavaBasicDataWrapperType(originalJavaBasicDataWrapper)) {
                Class<?> clazz = originalJavaBasicDataWrapper.getClass();
                try {
                    Method valueOfMethod = clazz.getMethod("valueOf",new Class<?> [] {String.class});
                    valueOfMethod.setAccessible(true);
                    cloneJavaBasicDataWrapper = (V)valueOfMethod.invoke(
                            null, new Object [] {originalJavaBasicDataWrapper.toString()});
                    valueOfMethod.setAccessible(false);
                } catch (NoSuchMethodException e) {
                    throw new CloneNotSupportedException(e);
                } catch (InvocationTargetException e) {
                    throw new CloneNotSupportedException(e);
                } catch (IllegalAccessException e) {
                    throw new CloneNotSupportedException(e);
                }
            } else {
                throw new CloneNotSupportedException("The clone target : " +
                        originalJavaBasicDataWrapper + " is not java basic data wrapper type.");
            }
        }

        return cloneJavaBasicDataWrapper;
    }

    public <V> V cloneArray (V arrayObject, Clone cloner) throws CloneNotSupportedException{
        V cloneArray = null;

        if (null != arrayObject) {
            Class<?> arrayObjectClass = arrayObject.getClass();
            if (arrayObjectClass.isArray() && (arrayObject instanceof Cloneable)) {
                try {
                    Method cloneMethod = Object.class.getDeclaredMethod(
                            ARRAY_CLONE_METHOD_NAME,CLONE_METHOD_PARAMETERS_TYPE);
                    cloneMethod.setAccessible(true);
                    cloneArray = (V) cloneMethod.invoke(arrayObject,CLONE_METHOD_PARAMETERS);
                    cloneMethod.setAccessible(false);

                    if (new byte [] {}.getClass().equals(arrayObjectClass) ||
                            new boolean [] {}.getClass().equals(arrayObjectClass) ||
                            new short [] {}.getClass().equals(arrayObjectClass) ||
                            new char [] {}.getClass().equals(arrayObjectClass) ||
                            new int [] {}.getClass().equals(arrayObjectClass) ||
                            new long [] {}.getClass().equals(arrayObjectClass) ||
                            new float [] {}.getClass().equals(arrayObjectClass) ||
                            new boolean [] {}.getClass().equals(arrayObjectClass)) {
                        List<?> list = Arrays.asList(cloneArray);
                        Object [] objects = new Object [list.size()];
                        for (int i=0; i<list.size(); i++) {
                            objects[i] = cloner.clone(list.get(i));
                        }
                        cloneArray = (V)objects;
                    }
                } catch (NoSuchMethodException e) {
                    throw new CloneNotSupportedException(e);
                } catch (InvocationTargetException e) {
                    throw new CloneNotSupportedException(e);
                } catch (IllegalAccessException e) {
                    throw new CloneNotSupportedException(e);
                }
            }
        }

        return cloneArray;
    }

    public <V> V cloneCollection (V originalCollection, Clone cloner) throws CloneNotSupportedException{
        V clonedCollection = null;

        if (null != originalCollection && originalCollection instanceof Collection) {
            Class<?> collectionClass = originalCollection.getClass();
            try {
                clonedCollection = (V)collectionClass.newInstance();
                Collection c = (Collection)clonedCollection;
                for (Object object : (Collection)originalCollection) {
                    c.add(cloner.clone(object));
                }
            } catch (InstantiationException e) {
                throw new CloneNotSupportedException(e);
            } catch (IllegalAccessException e) {
                throw new CloneNotSupportedException(e);
            }
        }

        return clonedCollection;
    }

    public <V> V cloneMap (V originalMap, Clone cloner) throws CloneNotSupportedException{
        V clonedMap = null;

        if (null != originalMap && originalMap instanceof Map) {
            Class<?> mapClass = originalMap.getClass();
            try {
                clonedMap = (V)mapClass.newInstance();
                Map map = (Map)clonedMap;
                Map original = (Map)originalMap;
                for (Object object : original.keySet()) {
                    map.put(cloner.clone(object),cloner.clone(original.get(object)));
                }
            } catch (InstantiationException e) {
                throw new CloneNotSupportedException(e);
            } catch (IllegalAccessException e) {
                throw new CloneNotSupportedException(e);
            }
        }

        return clonedMap;
    }

}
